/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    i2c.c
  * @brief   This file provides code for the configuration
  *          of the I2C instances.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "i2c.h"

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/* I2C2 init function */
void MX_I2C2_Init(void)
{

  /* USER CODE BEGIN I2C2_Init 0 */

  /* USER CODE END I2C2_Init 0 */

  LL_I2C_InitTypeDef I2C_InitStruct = {0};

  LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

  LL_RCC_SetI2CClockSource(LL_RCC_I2C123_CLKSOURCE_PCLK1);

  LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOB);
  /**I2C2 GPIO Configuration
  PB10   ------> I2C2_SCL
  PB11   ------> I2C2_SDA
  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_10|LL_GPIO_PIN_11;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_4;
  LL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /* Peripheral clock enable */
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C2);

  /* USER CODE BEGIN I2C2_Init 1 */

  /* USER CODE END I2C2_Init 1 */

  /** I2C Initialization
  */
  LL_I2C_EnableAutoEndMode(I2C2);
  LL_I2C_SetOwnAddress2(I2C2, 0, LL_I2C_OWNADDRESS2_NOMASK);
  LL_I2C_DisableOwnAddress2(I2C2);
  LL_I2C_DisableGeneralCall(I2C2);
  LL_I2C_EnableClockStretching(I2C2);
  I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
  I2C_InitStruct.Timing = 0x009033B1;
  I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
  I2C_InitStruct.DigitalFilter = 0;
  I2C_InitStruct.OwnAddress1 = 0;
  I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
  I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
  LL_I2C_Init(I2C2, &I2C_InitStruct);
  /* USER CODE BEGIN I2C2_Init 2 */

  /* USER CODE END I2C2_Init 2 */

}

/* USER CODE BEGIN 1 */
static int8_t I2C_WaitOnFlagUntilTimeout(I2C_TypeDef* I2Cx, uint32_t (*IsActiveFlag)(I2C_TypeDef*), __IO uint32_t Timeout_count)
{
    while (IsActiveFlag(I2Cx) == 0)
    {
        if (Timeout_count-- == 0)
        {
            return -1; // 超时
        }
    }
    return 0;
}


int8_t I2C_Master_Write_LL(I2C_TypeDef* I2Cx, uint8_t slave_addr, uint8_t* p_data, uint32_t len)
{
    __IO uint32_t timeout_counter;

    // 1. 等待总线变为空闲
    if (I2C_WaitOnFlagUntilTimeout(I2Cx, LL_I2C_IsActiveFlag_BUSY, I2C_LL_TIMEOUT_COUNT) != 0) return -1;

    // 2. 设置传输参数并生成 START + 写地址
    LL_I2C_HandleTransfer(I2Cx,
                          slave_addr << 1,
                          LL_I2C_ADDRSLAVE_7BIT,
                          len,
                          LL_I2C_MODE_AUTOEND,
                          LL_I2C_GENERATE_START_WRITE);

    // 3. 循环发送数据
    for (uint32_t i = 0; i < len; ++i)
    {
        // 等待发送缓冲区为空 (TXIS)
        if (I2C_WaitOnFlagUntilTimeout(I2Cx, LL_I2C_IsActiveFlag_TXIS, I2C_LL_TIMEOUT_COUNT) != 0) return -1;

        LL_I2C_TransmitData8(I2Cx, p_data[i]);
    }

    // 4. 等待STOP标志位 (因为是AUTOEND模式，硬件会自动发送STOP)
    timeout_counter = I2C_LL_TIMEOUT_COUNT;
    while (!LL_I2C_IsActiveFlag_STOP(I2Cx))
    {
        if (LL_I2C_IsActiveFlag_NACK(I2Cx)) {
             LL_I2C_ClearFlag_NACK(I2Cx);
             LL_I2C_ClearFlag_STOP(I2Cx);
             return -1; // 从机未应答
        }
        if (timeout_counter-- == 0) return -1;
    }

    // 5. 清除STOP标志位，准备下一次传输
    LL_I2C_ClearFlag_STOP(I2Cx);

    return 0; // 成功
}

int8_t I2C_Master_Read_LL(I2C_TypeDef* I2Cx, uint8_t slave_addr, uint8_t* p_data, uint32_t len)
{
    __IO uint32_t timeout_counter;

    // 1. 等待总线变为空闲
    if (I2C_WaitOnFlagUntilTimeout(I2Cx, LL_I2C_IsActiveFlag_BUSY, I2C_LL_TIMEOUT_COUNT) != 0) return -1;

    // 2. 设置传输参数并生成 START + 读地址
    LL_I2C_HandleTransfer(I2Cx,
                          slave_addr << 1,
                          LL_I2C_ADDRSLAVE_7BIT,
                          len,
                          LL_I2C_MODE_AUTOEND,
                          LL_I2C_GENERATE_START_READ);

    // 3. 循环接收数据
    for (uint32_t i = 0; i < len; ++i)
    {
        // 等待接收缓冲区非空 (RXNE)
        if (I2C_WaitOnFlagUntilTimeout(I2Cx, LL_I2C_IsActiveFlag_RXNE, I2C_LL_TIMEOUT_COUNT) != 0) return -1;

        p_data[i] = LL_I2C_ReceiveData8(I2Cx);
    }

    // 4. 等待STOP标志位
    timeout_counter = I2C_LL_TIMEOUT_COUNT;
    while (!LL_I2C_IsActiveFlag_STOP(I2Cx))
    {
        if (LL_I2C_IsActiveFlag_NACK(I2Cx)) {
             LL_I2C_ClearFlag_NACK(I2Cx);
             LL_I2C_ClearFlag_STOP(I2Cx);
             return -1;
        }
        if (timeout_counter-- == 0) return -1;
    }

    // 5. 清除STOP标志位
    LL_I2C_ClearFlag_STOP(I2Cx);

    return 0; // 成功
}


int8_t I2C_Master_WriteRead_LL(I2C_TypeDef* I2Cx, uint8_t slave_addr, uint8_t reg_addr, uint8_t* p_read_data, uint32_t len)
{
    if (I2C_Master_Write_LL(I2Cx, slave_addr, &reg_addr, 1) != 0)
    {
        return -1;
    }

    // 如果需要，可以在这里添加一个小的软件延时
    // for(volatile int i=0; i<1000; i++);

    if (I2C_Master_Read_LL(I2Cx, slave_addr, p_read_data, len) != 0)
    {
        return -1;
    }

    return 0;
}


/* USER CODE END 1 */
